#pragma once

#include "Parent.h"

Parent::Parent(VarTable* vt,StmtTable *st)
{
	myST = st;
	myVT = vt;
};

void Parent::set(INDEX stmtNo1, INDEX stmtNo2) {
	parentTableStmt.insert(std::make_pair<INDEX,INDEX>(stmtNo1,stmtNo2));
	++count;
};

Schema Parent::getSynSyn(DESIGN_ENTITY de1, NAME de1Name, DESIGN_ENTITY de2, NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	attNames.push_back(de2Name);
	Schema x(2,attNames);
	if(de1Name!=de2Name)
	{
		for (auto it = parentTableStmt.begin(); it != parentTableStmt.end(); ++it) {
			if ((myST->stmtTable.at(it->first) == de1 || de1 == "stmt") && (myST->stmtTable.at(it->second) == de2 || de2 == "stmt")) {
				vector<INDEX> vect;
				vect.push_back(it->first);
				vect.push_back(it->second);
				x.insertTuple(vect);
			}
		}
		return x;
	}
	else
	{
		return x;
	}
};

Schema Parent::getSynInt(DESIGN_ENTITY de1, NAME de1Name, INDEX para2)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	Schema x(1,attNames);
	for (auto it = parentTableStmt.begin(); it != parentTableStmt.end(); ++it) {
		if ((myST->stmtTable.at(it->first) == de1 || de1 == "stmt") && it->second==para2) {
			vector<INDEX> vect;
			vect.push_back(it->first);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Parent::getSynUnd(DESIGN_ENTITY de1, NAME de1Name)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	Schema x(1,attNames);
	for (auto it = parentTableStmt.begin(); it != parentTableStmt.end(); ++it) {
		if ((myST->stmtTable.at(it->first) == de1 || de1 == "stmt")) {
			vector<INDEX> vect;
			vect.push_back(it->first);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Parent::getIntSyn(INDEX para1, DESIGN_ENTITY de2, NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de2Name);
	Schema x(1,attNames);
	for (auto it = parentTableStmt.begin(); it != parentTableStmt.end(); ++it) {
		if (it->first==para1 && (myST->stmtTable.at(it->second) == de2 || de2 == "stmt")) {
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Parent::getIntInt(INDEX para1, INDEX para2)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = parentTableStmt.begin(); it != parentTableStmt.end(); ++it) {
		if ((it->first==para1) && (it->second==para2)) {
			vector<INDEX> vect;
			vect.push_back(para1);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Parent::getIntUnd(INDEX para1)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = parentTableStmt.begin(); it != parentTableStmt.end(); ++it) {
		if (it->first==para1) {
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Parent::getUndInt(INDEX para2)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = parentTableStmt.begin(); it != parentTableStmt.end(); ++it) {
		if ((it->second==para2)) {
			vector<INDEX> vect;
			vect.push_back(para2);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Parent::getUndSyn(DESIGN_ENTITY de2, NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de2Name);
	Schema x(1,attNames);
	for (auto it = parentTableStmt.begin(); it != parentTableStmt.end(); ++it) {
		if (myST->stmtTable.at(it->second) == de2 || de2 == "stmt") {
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Parent::getUndUnd()
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = parentTableStmt.begin(); it != parentTableStmt.end(); ++it) 
	{
		vector<INDEX> vect;
		vect.push_back(it->second);
		x.insertTuple(vect);
	}
	return x;
};

void Parent::computeClosure()
{
	pair <unordered_multimap<INDEX,INDEX>::iterator, unordered_multimap<INDEX,INDEX>::iterator> ret;
	unordered_set<INDEX> accum;
	bool isChanged;
	int initSize;
	for(int i = 1;i<=myST->getSize();i++)
	{
		isChanged = true;
		accum.clear();
		pair <unordered_multimap<INDEX,INDEX>::iterator, unordered_multimap<INDEX,INDEX>::iterator> ret;
		ret = parentTableStmt.equal_range(i);
		for (unordered_multimap<INDEX,INDEX>::iterator it=ret.first; it!=ret.second; ++it)
		{
			accum.insert(it->second);
		}
		while(isChanged)
		{
			isChanged = false;
			initSize = accum.size();
			
			for (auto it = accum.begin(); it != accum.end(); ++it)
			{
				pair <unordered_multimap<INDEX,INDEX>::iterator, unordered_multimap<INDEX,INDEX>::iterator> ret;
				ret = parentTableStmt.equal_range(*it);
				for (unordered_multimap<INDEX,INDEX>::iterator it=ret.first; it!=ret.second; ++it)
				{
					accum.insert(it->second);
				}
			}
			if(initSize!=accum.size())
			{
				isChanged = true;
			}
		}
		for (auto it = accum.begin(); it != accum.end(); ++it)
		{
			parentTableStmtClosure.insert((std::make_pair<INDEX,INDEX>(i,*it)));
		}
	}
};

Schema Parent::getSynSynClosure(DESIGN_ENTITY de1, NAME de1Name, DESIGN_ENTITY de2, NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	attNames.push_back(de2Name);
	Schema x(2,attNames);
	if(de1Name!=de2Name)
	{
		for (auto it = parentTableStmtClosure.begin(); it != parentTableStmtClosure.end(); ++it) {
			if ((myST->stmtTable.at(it->first) == de1 || de1 == "stmt") && (myST->stmtTable.at(it->second) == de2 || de2 == "stmt")) {
				vector<INDEX> vect;
				vect.push_back(it->first);
				vect.push_back(it->second);
				x.insertTuple(vect);
			}
		}
		return x;
	}
	else
	{
		return x;
	}
};

Schema Parent::getSynIntClosure(DESIGN_ENTITY de1, NAME de1Name, INDEX para2)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	Schema x(1,attNames);
	for (auto it = parentTableStmtClosure.begin(); it != parentTableStmtClosure.end(); ++it) {
		if ((myST->stmtTable.at(it->first) == de1 || de1 == "stmt") && it->second==para2) {
			vector<INDEX> vect;
			vect.push_back(it->first);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Parent::getSynUndClosure(DESIGN_ENTITY de1, NAME de1Name)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	Schema x(1,attNames);
	for (auto it = parentTableStmtClosure.begin(); it != parentTableStmtClosure.end(); ++it) {
		if ((myST->stmtTable.at(it->first) == de1 || de1 == "stmt")) {
			vector<INDEX> vect;
			vect.push_back(it->first);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Parent::getIntSynClosure(INDEX para1, DESIGN_ENTITY de2, NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de2Name);
	Schema x(1,attNames);
	for (auto it = parentTableStmtClosure.begin(); it != parentTableStmtClosure.end(); ++it) {
		if (it->first==para1 && (myST->stmtTable.at(it->second) == de2 || de2 == "stmt")) {
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Parent::getIntIntClosure(INDEX para1, INDEX para2)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = parentTableStmtClosure.begin(); it != parentTableStmtClosure.end(); ++it) {
		if ((it->first==para1) && (it->second==para2)) {
			vector<INDEX> vect;
			vect.push_back(para1);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Parent::getIntUndClosure(INDEX para1)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = parentTableStmtClosure.begin(); it != parentTableStmtClosure.end(); ++it) {
		if (it->first==para1) {
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Parent::getUndIntClosure(INDEX para2)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = parentTableStmtClosure.begin(); it != parentTableStmtClosure.end(); ++it) {
		if ((it->second==para2)) {
			vector<INDEX> vect;
			vect.push_back(para2);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Parent::getUndSynClosure(DESIGN_ENTITY de2, NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de2Name);
	Schema x(1,attNames);
	for (auto it = parentTableStmtClosure.begin(); it != parentTableStmtClosure.end(); ++it) {
		if (myST->stmtTable.at(it->second) == de2 || de2 == "stmt") {
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Parent::getUndUndClosure()
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = parentTableStmtClosure.begin(); it != parentTableStmtClosure.end(); ++it) 
	{
		vector<INDEX> vect;
		vect.push_back(it->second);
		x.insertTuple(vect);
	}
	return x;
};

void Parent::printAll() {
	cout << "Printing Parent Table" << endl;
	for (auto it = parentTableStmt.begin(); it != parentTableStmt.end(); ++it) {
		cout << it->first << " : " << it->second << endl;
	}
};